using System;
using System.Collections.Generic;
using System.Text;
using interfaces;
using retrofit;

namespace retrofit
{
	public struct measure
	{
		public string code;				// (sub)measure code
		public string[] text;			// measure text (EN, CN)
		public string[] description;	// measure description (EN, CN)
		public string[][] options;		// options text, 1D access via interf.BEE_Language_T
		public int numOptions;			// number of options
		public int control;				// control measure
		public int lowcost;				// low/no ocst measure
		public string defaultValue;		// default value
		public double[] cost;			// cost data (debug only, Delete this after debug)
	};

	public struct group
	{
		public string code;				// (sub)measure code
		public string[] text;			// measure text (EN, CN)
	};

    public static class Run
    {
		// program option index
		private static int[] m_options;

		// cross-influence factor matrix
		private static double[] m_factor = new double[(int)Keyword.energy_cat_t.NUM_ECT];
		
		// building area and area ratio array
		private static double[] m_areas = new double[(int)Building.BEE_BuildingFloorArea_T.NUM_BFAT];
		private static double[] m_ratios = new double[(int)Building.BEE_BuildingFloorArea_T.NUM_BFAT];

		// building energy rate array (electricity & gas)
		private static double[] m_energyRate = new double[(int)Keyword.energy_form_t.NUM_EFT];

		// query object to use for the whole program.
		private static Query m_query = new Query();

		// algorithm object.
		private static Algorithm m_al = new Algorithm(ref m_query);

		/// <summary>
		/// measure list containing relavent measure for selected building type.
		/// </summary>
		private static List<DmMeasure> m_measureList = new List<DmMeasure>();

		/// <summary>
		/// measure list dictionary list for mapping measure code to measure index.
		/// </summary>
		private static Dictionary<string, int> m_dictionaryList = new Dictionary<string, int>();

		/// <summary>
		/// measure group list
		/// </summary>
		private static List<DmMeasureGroup> m_groupList = new List<DmMeasureGroup>();

		/// <summary>
		/// measure group dictionary
		/// </summary>
		private static Dictionary<string, int> m_groupDictionaryList = new Dictionary<string, int>();

		/// <summary>
		/// constructor
		/// </summary>
        static Run()
        {
        }

		/// <summary>
		/// provide GUI default ECM cost data (material + labor).
		/// </summary>
		/// <param name="building_input">building information input, access via enum interf.BEE_BuildingInput_T </param>
		/// <param name="code">selected ECM code</param>
		/// <param name="instance_str">ECM instance stirng input, access via enum Cost.BEE_EcmCostStage_T (2 strings: before and after selections)</param>
 		/// <param name="err_msgs">the error msg return array</param>
		/// <returns>cost array, access via Cost.BEE_EcmCostCat_T</returns>
		public static double[] getDefaultCost(string code, string[] instance_str, ref string[] err_msgs)
		{
			// error message list.
			List<string> err_list = new List<string>();

//			err_list.Add("Enter program (This is not an error message): input measure code: " + code + ", input instance: " + instance_str[0] + "; " + instance_str[1]);
//					". Building input data is: " + building_input[0] + ", " + building_input[1] + ", " + building_input[2] + ", " + building_input[3] + ", " + building_input[4] + ", "
//					+ building_input[5] + ", " + building_input[6]);

			// cost data to return.
			double[] cost = new double[(int)Cost.BEE_EcmCostCat_T.NUM_ECMCCT];

			// error checking to make sure the ECM input string has the same dimension with type.
			if (instance_str.Length != (int)(int)Cost.BEE_EcmCostStage_T.NUM_ECMCST)
			{
				err_list.Add("An error happens at cost estimation process. The program should receive original and retrofit 2 input information.");
				err_msgs = copyErrMsg(err_list);
				return cost;
			}

			// if original and retrofit input is the same, we don't do cost analysis.
			if (instance_str[(int)Cost.BEE_EcmCostStage_T.Original_ECMCST] == instance_str[(int)Cost.BEE_EcmCostStage_T.Retrofit_ECMCST])
				return cost;

			int code_index;
			if (m_dictionaryList.ContainsKey(code))
			{
				code_index = m_dictionaryList[code];			// get measure's index
			}
			else
			{
				err_list.Add("Error when searching default cost data. Can not find related measure based on measure code input. Please check your measure code.");
				err_msgs = copyErrMsg(err_list);
				return cost;
			}

			// if we don't have error when tranlating input string to index, we continue query cost table
//			if (err_list.Count <= 0)
			{
				cost = queryUnitCost(code_index, instance_str, err_list);

				cost = scaleCost(code_index, cost);
			}

//			err_list.Add("Successfully finish querying default cost data. The cost data is: " + cost[0] + ", " + cost[1] + ", " + cost[2] + ".");

			// handle error messages
			appendErrMsg(ref err_msgs, err_list);
			
			return cost;
		}


		/// <summary>
		/// query unit material and labor cost based on keyword type (continuous/discrete)
		/// </summary>
		/// <param name="code_idx">ECM code integer index</param>
		/// <param name="instance_str">input ECM option, access via Cost.BEE_EcmCostCat_T</param>
		/// <param name="err_list">error list</param>
		/// <returns>return material and labor cost, access via Cost.BEE_EcmCostCat_T</returns>
		private static double[] queryUnitCost(int code_idx, string[] instance_str, List<string> err_list)
		{
			double[] cost = new double[(int)Cost.BEE_EcmCostCat_T.NUM_ECMCCT];

			DmMeasure measure = m_measureList[code_idx];

			// get continuous variables cost (boilor or chiller COP)
			// in the default cost, we assume that the cost is propotional to its COP value
			if (measure.dm_optionType == DmMeasure.InputOptionType[(int)DmMeasure.input_option_t.Continuous_IOT])
			{
//				double slope = Convert.ToDouble(measure.dm_costSlope);

				double cop = Convert.ToDouble(instance_str[(int)Cost.BEE_EcmCostStage_T.Retrofit_ECMCST]);
				cost[(int)Cost.BEE_EcmCostCat_T.Material_ECCT] = Convert.ToDouble(measure.dm_initiCost) + Convert.ToDouble(measure.dm_materialCost[0]) * cop; // *slope;
			}
			// get discrete variables cost
			else if (measure.dm_optionType == DmMeasure.InputOptionType[(int)DmMeasure.input_option_t.List_IOT])
			{
				int retro_idx = Convert.ToInt32(instance_str[(int)Cost.BEE_EcmCostStage_T.Retrofit_ECMCST]);

				// makre sure it is not the first option which is original condition
//				if (retro_idx != 0)
//				{
					// replacement cost type
					if (measure.dm_costType == Cost.BEE_EcmCostWork[(int)Cost.BEE_EcmCostWork_T.Replacement_ECMCT])
					{
						// for replacement cost, material cost = initial fixed material cost + variable material cost
						cost[(int)Cost.BEE_EcmCostCat_T.Material_ECCT] = Convert.ToDouble(measure.dm_initiCost) + Convert.ToDouble(measure.dm_materialCost[retro_idx]);
					}
					// addition cost type
					else if (measure.dm_costType == Cost.BEE_EcmCostWork[(int)Cost.BEE_EcmCostWork_T.Addition_ECMCT])
					{
						// for additional cost, we need to calculate the cost difference between retrofit and original
						int orig_idx = Convert.ToInt32(instance_str[(int)Cost.BEE_EcmCostStage_T.Original_ECMCST]);
						cost[(int)Cost.BEE_EcmCostCat_T.Material_ECCT] = Convert.ToDouble(measure.dm_initiCost) + (Convert.ToDouble(measure.dm_materialCost[retro_idx]) - Convert.ToDouble(measure.dm_materialCost[orig_idx]));
					}

					// O&M cost
					cost[(int)Cost.BEE_EcmCostCat_T.OM_ECCT] = Convert.ToDouble(measure.dm_omCost);
//				}
//				else
//				{
//					cost[(int)Cost.BEE_EcmCostCat_T.Material_ECCT] = 0;	// material cost
//					cost[(int)Cost.BEE_EcmCostCat_T.OM_ECCT] = 0;		// O&M cost
//				}
			}

			// if no material cost, no labor cost.
//			if (cost[(int)Cost.BEE_EcmCostCat_T.Material_ECCT] == 0)
//				cost[(int)Cost.BEE_EcmCostCat_T.Labor_ECCT] = 0;
//			else
				cost[(int)Cost.BEE_EcmCostCat_T.Labor_ECCT] = Convert.ToDouble(measure.dm_laborHour);
	

			return cost;
		}


		/// <summary>
		/// scale energy cost data based on building's area and area type
		/// </summary>
		/// <param name="areas">building area matrix</param>
		/// <param name="ratio">building floor area ratio</param>
		/// <param name="code_idx">ECM code integer index</param>
		/// <param name="unit_cost">unit cost array (material + labor hour)</param>
		/// <returns>final cost</returns>
		private static double[] scaleCost(int code_idx, double[] unit_cost)
		{
			double[] cost = new double[(int)Cost.BEE_EcmCostCat_T.NUM_ECMCCT];
			DmMeasure measure = m_measureList[code_idx];

			int area_type = Building.BEE_strBuildingFloorArea.IndexOf(measure.dm_appliedArea);
			if (area_type < 0)
				return cost;

			cost = unit_cost;

			// multiple labor rate.
			cost[(int)Cost.BEE_EcmCostCat_T.Labor_ECCT] *= Cost.BEE_laborRate;

			// for special area, we scale based on its area ratio.
			double area = m_areas[area_type];
			if(area_type >= (int)Building.BEE_BuildingFloorArea_T.LightingControl_BFAT)
				area = m_areas[(int)Building.BEE_BuildingFloorArea_T.Total_BFAT] * Building.BEE_AreaRatio[0][area_type];	// TODO, more building type

			// scale cost based on area number
			for (int i = 0; i < (int)Cost.BEE_EcmCostCat_T.NUM_ECMCCT; ++i)
			{
				cost[i] *= area;
				// cost should be bigger than 0.
				if (cost[i] < 0)
					cost[i] = 0;
			}

			return cost;
		}


        /// <summary>
        /// provide GUI default ECM energy and cost information data for bar chart view.
        /// </summary>
 		/// <param name="building_input">building information input, access via enum interf.BEE_BuildingInput_T </param>
		/// <param name="input_strs">the measure option string. This string will cover all retrofit and original selected input options</param>
		/// <param name="global">The energy ouptut global factor to scale simulated energy data to meet with measured data.</param>
		/// <param name="code">one selected ECM code string input</param>
		/// <param name="index_out">output ECM instance index output, the dimension vary with input Keyword. Please use Keyword.Keyword.m_inputs_selected_num[(int)kw] to create the array</param>
		/// <param name="energy_out">output ECM energy output (MWH = electricity + gas), the 1D is each case for current ECM options</param>
		/// <param name="economic_out">output economic data (total MWH and total cost), the 1D is each case for current ECM options, the 2D is total MWH and cost data</param>
        /// <param name="err_msgs">the error msg return array</param>
		public static void getEcmDefaultEnergy(double[] economic_opt, double[] building_input, string[][] input_strs, double[] global, string code, ref string[] index_out, ref double[] energy_out, ref double[][] economic_out, ref string[] err_msgs)
		{
			// +++++++++++++++++ TODO: put building type in the function input!!! ++++++++++++++++++++++++
//			string building_type = building_opt[(int)interf.BEE_AnalysisOption_T.BuildingType_AOT];

			// query section.
			List<String> err_list = new List<String>();
			
			// ** --------------- get measure's index -------------- ** //
			int index;
			if (m_dictionaryList.ContainsKey(code))
			{
				index = m_dictionaryList[code];			// get measure's index
			}
			else
			{
				err_list.Add("can't find input measure code, where calculate default measure saving and cost.");
				appendErrMsg(ref err_msgs, err_list);
				return;
			}

			// ** -------------- prepare current measure's cases -------------- ** //

			string[] cases;

			// special handling for Boilor COP continuous input
			if (code == "050101")
				cases = prepareContinuousStrings(0.6, 0.95, 0.05);
			// special handling for Chiller COP continuous input
			else if (code == "050201")
				cases = prepareContinuousStrings(3.4, 6.4, 0.5);
			else
				cases = prepareDiscreteStrings(m_measureList[index].dm_options.Count);

			index_out = cases;

			energy_out = new double[cases.Length];		// total energy amount when convert everything to kWh
			economic_out = new double[cases.Length][];	// economic output for 1) energy saving, 2) cost

			// ** ------------ parse building input ------------ ** //
			double[][] factors = new double[(int)Keyword.stage_t.NUM_ST][];

			int[][] empirical = new int[(int)Keyword.stage_t.NUM_ST][]
			{
				new int[Keyword.m_input_code.Length],
				new int[Keyword.m_input_code.Length]
			};

			string[][] simulated = new string[(int)Keyword.stage_t.NUM_ST][]
			{
				new string[(int)Keyword.keyword_t.NUM_KWT],
				new string[(int)Keyword.keyword_t.NUM_KWT]
			};

			string[][] input_strs_new = new string[(int)Keyword.stage_t.NUM_ST][]
			{
				input_strs[(int)Keyword.stage_t.ORIGINAL_ST],
				new string[(int)Keyword.m_input_code.Length]
			};

			// the original case string is always the first one in the list.
			input_strs_new[(int)Keyword.stage_t.ORIGINAL_ST][index] = cases[0];

			// copy original to the new input string to maintain the same source.
			Array.Copy(input_strs_new[(int)Keyword.stage_t.ORIGINAL_ST], input_strs_new[(int)Keyword.stage_t.RETROFIT_ST], input_strs[(int)Keyword.stage_t.ORIGINAL_ST].Length);

			// ** ================= start original calculation ================= ** //
			
			// we only parse original stage input first.
			parseAnInput((int)Keyword.stage_t.ORIGINAL_ST, input_strs, building_input, ref empirical, ref simulated, err_list);

			// we only calculate original stage empirical factor first, since retrofit stage will vary case by case.
			// And we fix retrofit empirical factor the same as retrofit factor, and just vary simulated variable first.
			factors[(int)Keyword.stage_t.ORIGINAL_ST] = calculateEnergyFactor(empirical[(int)Keyword.stage_t.ORIGINAL_ST], err_list);

			// we only index original input.
			Input input_orig = new Input();
			input_orig.createInputIdx(simulated[(int)Keyword.stage_t.ORIGINAL_ST], err_list);

			// 1st of all, we calculate the original case energy only.
			Output output_orig = new Output();
			// calculate once for original case only
			output_orig = calculateEnergyOnce(building_input, input_orig, factors[(int)Keyword.stage_t.ORIGINAL_ST], global, err_list);

			// we need total energy output for measure's each option
			double[][] out_total = new double[cases.Length][];
			// the first output is always the original case.
			out_total[0] = outputEnergy(output_orig);

			// store result for output
			energy_out[0] = out_total[0][(int)interf.BEE_Energy_T.EnergyAmount_ET];
			economic_out[0] = new double[] {0, 0};	// for saving and cost

//			Console.WriteLine("Original energy total view:");
//			printArray(out_total[0][(int)interf.BEE_Energy_T.EnergyCost_ET]);
//			Console.WriteLine("");

			// string input for query default cost.
			string[] ecm_str = new string[(int)Keyword.stage_t.NUM_ST] {input_strs_new[(int)Keyword.stage_t.ORIGINAL_ST][index], input_strs_new[(int)Keyword.stage_t.ORIGINAL_ST][index]};

			// ** ============== start to do retrofit cases calculation.============= ** //
			
			// we start from original case.
			for (int i = 1; i < cases.Length; ++i)
			{
				// --------- prepare input/ouput section --------- //

				// construct measure new input by pulling data from measure's each option
				input_strs_new[(int)Keyword.stage_t.RETROFIT_ST][index] = cases[i];

				// we only process retrofit stage input.
				parseAnInput((int)Keyword.stage_t.RETROFIT_ST, input_strs_new, building_input, ref empirical, ref simulated, err_list);

				// create retrofit empirical factor
				factors[(int)Keyword.stage_t.RETROFIT_ST] = calculateEnergyFactor(empirical[(int)Keyword.stage_t.RETROFIT_ST], err_list);
				validateEnergyFactor(ref factors);

				// create input index for retrofit
				Input input_retro = new Input();
				input_retro.createInputIdx(simulated[(int)Keyword.stage_t.RETROFIT_ST], err_list);

				// --------- algorithm calculation section ---------- //

				Output output_retro = new Output();
				output_retro = calculateEnergyOnce(building_input, input_retro, factors[(int)Keyword.stage_t.RETROFIT_ST], global, err_list);

				// -------------- query default cost ---------------- //

				ecm_str[(int)Keyword.stage_t.RETROFIT_ST] = cases[i];
				double[] ecm_cost = getDefaultCost(code, ecm_str, ref err_msgs);
				
				// --------- start energy output processing --------- //

				out_total[i] = outputEnergy(output_retro);

//				Console.WriteLine("Diff energy total view:");
//				printArray(out_total[i][(int)interf.BEE_Energy_T.EnergyCost_ET]);
//				Console.WriteLine("");

				energy_out[i] = out_total[i][(int)interf.BEE_Energy_T.EnergyAmount_ET];

				economic_out[i] = diffOutEnergy(economic_opt, ecm_cost, out_total[0][(int)interf.BEE_Energy_T.EnergyCost_ET], out_total[i][(int)interf.BEE_Energy_T.EnergyCost_ET]);

				// TODO, the cost matrix should be 1D dimension for one single measure.
//				runEconomic(input_strs_new, economic_opt, ecm_cost, economic_out[i], err_list);

//				Console.WriteLine("Difference energy total view:");
//				printArray(economic_out[i]);
//				Console.WriteLine("");
			}

			// handle error messages
			appendErrMsg(ref err_msgs, err_list);

			// TODO, for each index, sizing based on input floor area.
		}


		/// <summary>
		/// run the whole data processing.
		/// </summary>
        /// <param name="economic_opt">economic input array, access by using enum Building.BEE_BuildingEconomic_T </param>
		/// <param name="building_input">building information input, access via enum interf.BEE_BuildingInput_T </param>
		/// <param name="input_strs">ECM string inputs, 1D access via enum Cost.BEE_EcmCostStage_T, 2D is input for all measure code access via Keyword.m_input_code</param>
		/// <param name="global_factor">The energy ouptut global factor to scale simulated energy data to meet with measured data.</param>
		/// <param name="ecm_cost">ECM cost input array, 1D access via Keyword.keyword_t, 2D access via Cost.BEE_EcmCostCat_T</param>
		/// <param name="energy_cat">category energy output, use interf.BEE_Energy_T to access 1st dimension, interf.BEE_Output_T for 2nd dimension and use Keyword.energy_cat_t for 3rd dimension</param>
		/// <param name="energy_tot">total energy output, use interf.BEE_Energy_T to access 1st dimension, interf.BEE_Output_T for 2nd dimension and use Keywrod.energy_form_t for 3rd dimension</param>
		/// <param name="economic_out">economic results output, access via Cost.BEE_EconomyResult_T </param>
		/// <param name="err_msgs">the error msg return array</param>
		public static void run(double[] economic_opt, double[] building_input, string[][] input_strs, double[] global_factor,
							double [][] ecm_cost, ref double[][][] energy_cat, ref double[][][] energy_tot, ref double[] economic_out, ref string[] err_msgs)
        {
			// +++++++++++++++++ TODO: put building type in the function input!!! ++++++++++++++++++++++++
//			string building_type = building_opt[(int)interf.BEE_AnalysisOption_T.BuildingType_AOT];

            // error message list.
            List<string> err_list = new List<string>();

			//** --------- prepare input/ouput section --------- **//

			// we do this for each output type
            List<Output> outputs = new List<Output>();
            for (int i = 0; i < (int)interf.BEE_Output_T.NUM_OT; ++i)
            {
                outputs.Add(new Output());
            }

			//  ------------ parse building input ------------ //
			double[][] factors = new double[(int)Keyword.stage_t.NUM_ST][];

			int[][] empirical = new int[(int)Keyword.stage_t.NUM_ST][]
			{
				new int[Keyword.m_input_code.Length],
				new int[Keyword.m_input_code.Length]
			};

			string[][] simulated = new string[(int)Keyword.stage_t.NUM_ST][]
			{
				new string[(int)Keyword.keyword_t.NUM_KWT],
				new string[(int)Keyword.keyword_t.NUM_KWT]
			};

			// parsing input variables to simulated and empirical.
			parseInputs(input_strs, building_input, ref empirical, ref simulated, err_list);

			// calculate empirical energy factor
			for (int i = 0; i < (int)Keyword.stage_t.NUM_ST; ++i)
			{
				factors[i] = calculateEnergyFactor(empirical[i], err_list);
			}

			// create option index from string option input
			List<Input>	list_inputs = indexingInputs(simulated, err_list);

			//** --------- algorithm calculation section --------- **//

			outputs = calculateEnergy(building_input, list_inputs, factors, global_factor, err_list);


			//** --------- start energy output processing --------- **//

			// output energy
			outputEnergy(ref outputs, economic_opt, energy_cat, energy_tot);       

			//** ---------- cost calculation -------------- **/
			economic_out[(int)Cost.BEE_EconomyResult_T.EnergySaving_ERT] = 
				energy_tot[(int)interf.BEE_Energy_T.EnergyCost_ET][(int)interf.BEE_Output_T.Difference_OT][(int)Keyword.energy_form_t.Electricity_EFT] +	
				energy_tot[(int)interf.BEE_Energy_T.EnergyCost_ET][(int)interf.BEE_Output_T.Difference_OT][(int)Keyword.energy_form_t.Gas_EFT];

			economic_out = runEconomic(input_strs, economic_opt, ecm_cost, economic_out, err_list);

            // convert error messages from list<string> to string[]
			 appendErrMsg(ref err_msgs, err_list);
		}


		/// <summary>
		/// run each ECM (different from original and retrofit), and output ECM's cost, energy saving and payback period.
		/// </summary>
		/// <param name="economic_opt">economic option string input, access via BEE_BuildingEconomic_T</param>
		/// <param name="building_input">building information input, access via enum interf.BEE_BuildingInput_T </param>
		/// <param name="input_strs">ECM string inputs, 1D access via enum Cost.BEE_EcmCostStage_T, 2D is input for all measure code access via Keyword.m_input_code</param>
		/// <param name="global">The energy ouptut global factor to scale simulated energy data to meet with measured data.</param>
		/// <param name="ecm_cost">ECM cost input array, 1D access via Keyword.keyword_t, 2D access via Cost.BEE_EcmCostCat_T</param>
		/// <param name="economic_out">economic results output, 1D access via Keyword.keyword_t, 2D access via Cost.BEE_EconomyResult_T</param>
		/// <param name="err_msgs">the error msg return array</param>
		public static void runSingleEcm(double[] economic_opt, double[] building_input, string[][] input_strs, double[] global,
							double[][] ecm_cost, ref double[][] economic_out, ref string[] err_msgs)
		{
			// +++++++++++++++++ TODO: put building type in the function input!!! ++++++++++++++++++++++++
//			string building_type = building_opt[(int)interf.BEE_AnalysisOption_T.BuildingType_AOT];

			List<string> err_list = new List<string>();

			//  ------------ parse building input ------------ //
			double[][] factors = new double[(int)Keyword.stage_t.NUM_ST][];

			int[][] empirical = new int[(int)Keyword.stage_t.NUM_ST][]
			{
				new int[Keyword.m_input_code.Length],
				new int[Keyword.m_input_code.Length]
			};

			string[][] simulated = new string[(int)Keyword.stage_t.NUM_ST][]
			{
				new string[(int)Keyword.keyword_t.NUM_KWT],
				new string[(int)Keyword.keyword_t.NUM_KWT]
			};

			//** ---------------------- ORIGINAL STAGE HANDLING ----------------------- ** //

			// we only parse original stage input first.
			parseAnInput((int)Keyword.stage_t.ORIGINAL_ST, input_strs, building_input, ref empirical, ref simulated, err_list);

			// we only calculate original stage empirical factor first, since retrofit stage will vary case by case.
			// And we fix retrofit empirical factor the same as retrofit factor, and just vary simulated variable first.
			factors[(int)Keyword.stage_t.ORIGINAL_ST] = calculateEnergyFactor(empirical[(int)Keyword.stage_t.ORIGINAL_ST], err_list);

			// we only index original input.
			Input input_orig = new Input();
			input_orig.createInputIdx(simulated[(int)Keyword.stage_t.ORIGINAL_ST], err_list);

			// 1st of all, we calculate the original case energy only.
			Output output_orig = new Output();
			// calculate once for original case only
			output_orig = calculateEnergyOnce(building_input, input_orig, factors[(int)Keyword.stage_t.ORIGINAL_ST], global, err_list);

			//** ---------------------- RETROFIT STAGE HANDLING ----------------------- ** //

			// the new iput string list
			string[][] input_strs_new = { input_strs[(int)Keyword.stage_t.ORIGINAL_ST], new string[input_strs[(int)Keyword.stage_t.ORIGINAL_ST].Length] };
			// copy original to retrofit to maintain the same source
			Array.Copy(input_strs[(int)Keyword.stage_t.ORIGINAL_ST], input_strs_new[(int)Keyword.stage_t.RETROFIT_ST], input_strs[(int)Keyword.stage_t.ORIGINAL_ST].Length);

			// ** 2nd, we vary all the simulated variables case by case 
			// loop over all the measure code to see if there is any difference between the origianl and retrofit buildng.
			for (int i = 0; i < (int)Keyword.m_input_code.Length; ++i)
			{
				// make sure you have an index change for retrofit
				if (input_strs[(int)Keyword.stage_t.ORIGINAL_ST][i] != input_strs[(int)Keyword.stage_t.RETROFIT_ST][i])
				{
					//** --------- prepare input/ouput section --------- **//

					input_strs_new[(int)Keyword.stage_t.RETROFIT_ST][i] = input_strs[(int)Keyword.stage_t.RETROFIT_ST][i];

					// we only parse original stage input first.
					parseAnInput((int)Keyword.stage_t.RETROFIT_ST, input_strs_new, building_input, ref empirical, ref simulated, err_list);

					// create retrofit empirical factor
					factors[(int)Keyword.stage_t.RETROFIT_ST] = calculateEnergyFactor(empirical[(int)Keyword.stage_t.RETROFIT_ST], err_list);
					validateEnergyFactor(ref factors);

					// create input index for retrofit
					Input input_retro = new Input();
					input_retro.createInputIdx(simulated[(int)Keyword.stage_t.RETROFIT_ST], err_list);

					//** --------- algorithm calculation section --------- **//
					Output output_retro = new Output();
					output_retro = calculateEnergyOnce(building_input, input_retro, factors[(int)Keyword.stage_t.RETROFIT_ST], global, err_list);

					//** --------- start energy output processing --------- **//

					double[][][] energy_cat = new double[(int)interf.BEE_Energy_T.NUM_ET][][];
					double[][][] energy_tot = new double[(int)interf.BEE_Energy_T.NUM_ET][][];

					// re-construct output list, by putting the original energy and retrofit energy into the list
					Output output_orig_new = new Output();
					Array.Copy(output_orig.m_energy, output_orig_new.m_energy, output_orig.m_energy.Length);

					List<Output> outputs = prepareOutputList();
					outputs[(int)interf.BEE_Output_T.Original_OT] = output_orig_new;
					outputs[(int)interf.BEE_Output_T.Retrofit_OT] = output_retro;

					outputEnergy(ref outputs, economic_opt, energy_cat, energy_tot);

//					Console.WriteLine("Original energy total:");
//					printArray(outputs[0].m_energyTotalCost);
//					Console.WriteLine("");

//					Console.WriteLine("Retrofit energy total:");
//					printArray(outputs[1].m_energyTotalCost);
//					Console.WriteLine("");

//					Console.WriteLine("Energy saving total:");
//					printArray(outputs[2].m_energyTotalCost);
//					Console.WriteLine("");

					// get energy savings
					economic_out[i] = new double[(int)Cost.BEE_EconomyResult_T.NUM_ERT];

					economic_out[i][(int)Cost.BEE_EconomyResult_T.EnergySaving_ERT] = 
						energy_tot[(int)interf.BEE_Energy_T.EnergyCost_ET][(int)interf.BEE_Output_T.Difference_OT][(int)Keyword.energy_form_t.Electricity_EFT] +	
						energy_tot[(int)interf.BEE_Energy_T.EnergyCost_ET][(int)interf.BEE_Output_T.Difference_OT][(int)Keyword.energy_form_t.Gas_EFT];

					// construct input string array for economic analysis.
					string[][] input_str = new string[][] { input_strs_new[(int)Keyword.stage_t.ORIGINAL_ST], input_strs_new[(int)Keyword.stage_t.RETROFIT_ST] };

					// TODO, since this is only single ECM analysis, construct a single ECM economic calculation method 
					// instead of sending all ECMs into the method.
					economic_out[i] = runEconomic(input_str, economic_opt, ecm_cost, economic_out[i], err_list);

//					Console.WriteLine("Payback period:");
//					printArray(economic_out[i][]);
//					Console.WriteLine("");

					// convert error messages from list<string> to string[]
					// err_msgs = copyErrMsg(err_list);

					// restore back the measure variable for retrofit using original measure
					input_strs_new[(int)Keyword.stage_t.RETROFIT_ST][i] = input_strs[(int)Keyword.stage_t.ORIGINAL_ST][i];
				}
			}

			// copy error message
			appendErrMsg(ref err_msgs, err_list);
		}


		/// <summary>
		/// calculate simulated results uncertain level, by comparing with measured data.
		/// </summary>
		/// <param name="simulated">before retrofit simulated data (electricity [MWH], natural gas [GJ])</param>
		/// <param name="measured">actural use measured data (electricity [MWH], natural gas [GJ])</param>
		/// <returns>the error when comparing simulated with measured data</returns>
		public static double runUncertainty(double[] simulated, double[] measured)
		{
			// natural gas energy should be converted from GJ to kWh.
			double simulate_total = simulated[(int)Keyword.energy_form_t.Electricity_EFT] + simulated[(int)Keyword.energy_form_t.Gas_EFT]/3.6;
			double measure_total = measured[(int)Keyword.energy_form_t.Electricity_EFT] + measured[(int)Keyword.energy_form_t.Gas_EFT]/3.6;

			return (simulate_total - measure_total) / measure_total;
		}

		/// <summary>
		/// calculate building total economic analysis -- total cost, payback period. The total energy saving data is an input
		/// </summary>
		/// <param name="input_strs">ECM string inputs, 1D access via enum Cost.BEE_EcmCostStage_T, 2D access via Keyword.keyword_t</param>
		/// <param name="economic_opt">economic input array, access by using enum Building.BEE_BuildingEconomic_T </param>
		/// <param name="ecm_cost">Each ECM's material and labor cost, 1D access via Keyword.keyword_t, 2D access via Cost.BEE_EcmCostCat_T</param>
		/// <param name="economic">Economic data, access via Cost.BEE_EconomyResult_T</param>
		/// <param name="err_msgs">error message</param>		
		/// <returns>economic output, access via Cost.BEE_EconomyResult_T</returns>
		private static double[] runEconomic(string[][] input_strs, double[] eco_opt, double[][] ecm_cost, double[] economic, List<string> err_list)
		{
			// List<string> err_list = new List<string>();
            double[] results = economic;
			double[] cost = new double[(int)Cost.BEE_EcmCostCat_T.NUM_ECMCCT];


			if (ecm_cost.Length != m_measureList.Count)
			{
				err_list.Add("Error! The input ECM number for cost data does not match with program required ECM number.");
				// err_msgs = copyErrMsg(err_list);
                return results;
			}

			if (economic.Length != (int)Cost.BEE_EconomyResult_T.NUM_ERT)
			{
				err_list.Add("The output ecomonic data should be dimension of " + (int)Cost.BEE_EconomyResult_T.NUM_ERT + ".");
				// err_msgs = copyErrMsg(err_list);
                return results;
			}

			//double total_cost = 0;
			for (int i = 0; i < (int)m_measureList.Count; ++i)
			{
				if (ecm_cost[i].Length != (int)Cost.BEE_EcmCostCat_T.NUM_ECMCCT)
				{
					err_list.Add("Error! The input ECM number for cost data does not match with program required ECM number.");
					// err_msgs = copyErrMsg(err_list);
                    return results;
				}
				// if we have different input index, we need to calculate this ECM's retrofit cost.
				// make sure you have an index change for retrofit
				if (input_strs[(int)Keyword.stage_t.ORIGINAL_ST][i] != input_strs[(int)Keyword.stage_t.RETROFIT_ST][i])
				{
					for (int j = 0; j < (int)Cost.BEE_EcmCostCat_T.NUM_ECMCCT; ++j)
					{
//                        results[(int)Cost.BEE_EconomyResult_T.TotalCost_ERT] += ecm_cost[i][j];
						cost[j] += ecm_cost[i][j];
					}
				}
			}

			//economic[(int)Cost.BEE_EconomyResult_T.TotalCost_ERT] = total_cost;

			// if we don't have an energy efficiency retrofit
//			if (results[(int)Cost.BEE_EconomyResult_T.EnergySaving_ERT] > 0)
//				results[(int)Cost.BEE_EconomyResult_T.PayBackPeriod_ERT] = 999999;	// take forever to get your return back.
//			else
//			{
				// TODO, this is debug only when we don't have full cost data.
				// we give an arbitary saving ratio and back calculate cost data from energy saving.
//				if (economic[(int)Cost.BEE_EconomyResult_T.TotalCost_ERT] < 0 && economic[(int)Cost.BEE_EconomyResult_T.EnergySaving_ERT] < 0)
//					economic[(int)Cost.BEE_EconomyResult_T.TotalCost_ERT] = - economic[(int)Cost.BEE_EconomyResult_T.EnergySaving_ERT] * 0.8;
				results = Algorithm.payBackPeriod(eco_opt, cost, economic, err_list);
//			}
            //err_msgs = copyErrMsg(err_list);

            return results;
		}


		/// <summary>
		/// overload function to provide string[] error message inputs
		/// </summary>
		/// <param name="input_strs">ECM string inputs, 1D access via enum Cost.BEE_EcmCostStage_T, 2D access via Keyword.keyword_t</param>
		/// <param name="economic_opt">economic input array, access by using enum Building.BEE_BuildingEconomic_T </param>
		/// <param name="ecm_cost">Each ECM's material and labor cost, 1D access via Keyword.keyword_t, 2D access via Cost.BEE_EcmCostCat_T</param>
		/// <param name="economic">Economic data, access via Cost.BEE_EconomyResult_T</param>
		/// <param name="err_msgs">error message</param>		
		/// <returns>economic output, access via Cost.BEE_EconomyResult_T</returns>
		public static double[] runEconomic(string[][] input_strs, double[] eco_opt, double[][] ecm_cost, double[] economic, ref string[] err_msgs)
		{ 
			List<string> err_list = new List<string>();
			double[] results = runEconomic(input_strs, eco_opt, ecm_cost, economic, err_list);
			
			appendErrMsg(ref err_msgs, err_list);
			
			return results;
		}


		/// <summary>
		/// convert system option string input to index input
		/// </summary>
		/// <param name="option_str">option string input</param>
		/// <param name="err_list">error list</param>
		/// <returns>option indeics</returns>
		private static int[] indexingOptions(string[] option_str, List<string> err_list)
		{
			int[] opts = new int[(int)interf.BEE_AnalysisOption_T.NUM_AOT];

			for (int i = 0; i < (int)interf.BEE_AnalysisOption_T.NUM_AOT; ++i)
			{
				bool found = false;
				for (int j = 0; j < interf.BEE_strAnalysisOptions.Length; j++)
				{
					if (option_str[i] == interf.BEE_strAnalysisOptions[i][j])
					{
						opts[i] = j;
						found = true;
						break;
					}
				}

				// can't find mathing option input string, we give an error msg
				if (found == false)
					err_list.Add("Can not find parameter option input for " + interf.BEE_strAnalysisOptionType[i] + "! Please check you parameter option input.");
			}

			return opts;
		}


		/// <summary>
		/// create input object list based on original and retrofit string input
		/// </summary>
		/// <param name="input_strs">ECM string inputs (original and retrofit)</param>
		/// <param name="err_list">error list</param>
		/// <returns>the input list for original and retrofit</returns>
		private static List<Input> indexingInputs(string[][] input_strs, List<string> err_list)
		{
			List<Input> list_input = new List<Input>();

			// create input index first

			for (int i = 0; i < (int)Cost.BEE_EcmCostStage_T.NUM_ECMCST; ++i )
			{
				Input input = new Input();
				input.createInputIdx(input_strs[i], err_list);
				list_input.Add(input);
			}

			return list_input;
		}


		/// <summary>
		/// calculate buildng energy consumption output results
		/// </summary>
		/// <param name="opts">option indeics</param>
		/// <param name="building_input">building information input, access via enum interf.BEE_BuildingInput_T </param>
		/// <param name="outlist">the output list get from queryTable()</param>
		/// <param name="outputs">energy output results we </param>
		/// <returns>return energy calculation output results, access via interf.BEE_Output_T</returns>
		private static List<Output> calculateEnergy(double[] building_input, List<Input> list_inputs, double[][] empirical, double[] global, List<string> err_list)
		{
			List<Output> outputs = prepareOutputList();

			// process query results and scale energy output
			for (int i = 0; i < (int)Cost.BEE_EcmCostStage_T.NUM_ECMCST; ++i)
			{
				outputs[i] = calculateEnergyOnce(building_input, list_inputs[i], empirical[i], global, err_list);
			}

			// scale energy result by using floor area
//			outputs = Algorithm.sizingBuilding((interf.BEE_Building_T)m_options[(int)interf.BEE_AnalysisOption_T.BuildingType_AOT], m_areas, outputs);

			return outputs;
		}


		/// <summary>
		/// do energy calculation once for either original or retrofit
		/// </summary>
		/// <param name="building_input"></param>
		/// <param name="input"></param>
		/// <param name="empirical"></param>
		/// <param name="err_list"></param>
		/// <returns></returns>
		private static Output calculateEnergyOnce(double[] building_input, Input input, double[] empirical, double[] global, List<string> err_list)
		{
			string building_type = interf.BEE_strAnalysisOptions[(int)interf.BEE_AnalysisOption_T.BuildingType_AOT]
				[m_options[(int)interf.BEE_AnalysisOption_T.BuildingType_AOT]];

			Output output = m_al.process(building_type, input, empirical, global, err_list);

			// if no refrigeration system, we need to scale energy and force refrigeration energy = 0
			if (building_input[(int)interf.BEE_BuildingInput_T.Refrigeration_BIT] <= 0)
				m_al.processRefrigeration(building_type, output, err_list);

			Output out1 = Algorithm.sizingBuilding((interf.BEE_Building_T)m_options[(int)interf.BEE_AnalysisOption_T.BuildingType_AOT], m_areas, output);

			return out1;
		}


		/// <summary>
		/// Prepare building areas and area ratios for the calculation. 
		/// Call this function before you call any public run function in this class.
		/// </summary>
		/// <param name="building_input"></param>
		public static void prepareBuildingArea(double[] building_input)
		{
			// process building area information.
			m_areas[(int)Building.BEE_BuildingFloorArea_T.Total_BFAT] = building_input[(int)interf.BEE_BuildingInput_T.TotalArea_BIT];
			m_areas[(int)Building.BEE_BuildingFloorArea_T.Conditioned_BFAT] = building_input[(int)interf.BEE_BuildingInput_T.ConditionedArea_BIT];
			m_ratios[(int)Building.BEE_BuildingFloorArea_T.Window_BFAT] = building_input[(int)interf.BEE_BuildingInput_T.WWR_BIT];

			// TODO, more refrigeration option.
			if (building_input[(int)interf.BEE_BuildingInput_T.Refrigeration_BIT] <= 0)
				m_ratios[(int)Building.BEE_BuildingFloorArea_T.Refrigeration_BFAT] = 0;
			else
				m_ratios[(int)Building.BEE_BuildingFloorArea_T.Refrigeration_BFAT] = 1.0;

			// extrapolate building areas
			m_areas = Building.extrpolateAreas((interf.BEE_Building_T)m_options[(int)interf.BEE_AnalysisOption_T.BuildingType_AOT], m_areas, m_ratios);
		}


		/// <summary>
		/// prepare output list for energy calculation output
		/// </summary>
		/// <returns></returns>
		private static List<Output> prepareOutputList()
		{
			List<Output> outputs = new List<Output>();
			for (int j = 0; j < (int)interf.BEE_Output_T.NUM_OT; ++j)
			{
				outputs.Add(new Output());
			}
			
			return outputs;
		}

		/// <summary>
		/// query electricity energy rate and calculate electricity and gas annual average energy rate.
		/// </summary>
		/// <param name="err_list">error list</param>
		/// <returns>energy rate, access via Keyword.energy_form_t</returns>
		private static void queryEnergyRate(List<string>err_list)
		{
			//** --------- start energy output processing --------- **//

			// energy rate resutls to be queried
			double[] eleRate = new double[(int)Energy.BEE_ElectricityRate_T.NUM_ERT];

			// query electricity rate table,
			// here, we only input Shanghai climate zone
			// TODO, user-input climate zone.
			interf.BEE_ClimateZone_T climate = interf.BEE_ClimateZone_T.HotSummerColdWinter_CZT;
			m_query.queryTable(climate, eleRate, err_list);

			// TODO, more building type based on user input
			m_energyRate[(int)Keyword.energy_form_t.Electricity_EFT] = Energy.avgEleRate(eleRate, interf.BEE_Building_T.ShoppingMall_BT);
			m_energyRate[(int)Keyword.energy_form_t.Gas_EFT] = Energy.BEE_gasRate;
		}

		/// <summary>
		/// output energy results
		/// </summary>
		/// <param name="outputs">building energy consumption results, get from calculateEnergy(), access via interf.BEE_Output_T</param>
		/// <param name="energyRate">energy rate query results, get from queryEnergyRate()</param>
		/// <param name="economic_opt">economic analysis options from user input, access via Building.BEE_BuildingEconomic_T</param>
		/// <param name="energy_cat">energy category output</param>
		/// <param name="energy_tot">energy total output</param>
		private static void outputEnergy(ref List<Output> outputs, double[] economic_opt, double[][][] energy_cat, double[][][] energy_tot)
		{
			// unit conversion
			outputs[(int)interf.BEE_Output_T.Original_OT].unitConversion();
			outputs[(int)interf.BEE_Output_T.Retrofit_OT].unitConversion();

			// calculate cost
			outputs[(int)interf.BEE_Output_T.Original_OT].calcEnergyCost(m_energyRate);
			outputs[(int)interf.BEE_Output_T.Retrofit_OT].calcEnergyCost(m_energyRate);

			// calculate difference energy
			outputs[(int)interf.BEE_Output_T.Difference_OT] = outputs[(int)interf.BEE_Output_T.Retrofit_OT] - outputs[(int)interf.BEE_Output_T.Original_OT];

			// calculate energy saving ratio
			outputs[(int)interf.BEE_Output_T.SavingRatio_OT] = outputs[(int)interf.BEE_Output_T.Difference_OT] / outputs[(int)interf.BEE_Output_T.Original_OT];

			// calculate life cycle energy ouptut
			outputs[(int)interf.BEE_Output_T.LifeCycle_OT] = Output.calcLifeCycleOutput(outputs[(int)interf.BEE_Output_T.Difference_OT], economic_opt);

			// calculate total energy
			for (int i = 0; i < (int)interf.BEE_Output_T.NUM_OT; ++i)
			{
				outputs[i].calcTotalEnergy();
			}

			energy_cat[(int)interf.BEE_Energy_T.EnergyAmount_ET] = new double[(int)interf.BEE_Output_T.NUM_OT][];
			energy_tot[(int)interf.BEE_Energy_T.EnergyAmount_ET] = new double[(int)interf.BEE_Output_T.NUM_OT][];
			energy_cat[(int)interf.BEE_Energy_T.EnergyCost_ET] = new double[(int)interf.BEE_Output_T.NUM_OT][];
			energy_tot[(int)interf.BEE_Energy_T.EnergyCost_ET] = new double[(int)interf.BEE_Output_T.NUM_OT][];

			// transfer to output array.
			for (int i = 0; i < (int)interf.BEE_Output_T.NUM_OT; ++i)
			{
				energy_cat[(int)interf.BEE_Energy_T.EnergyAmount_ET][i] = outputs[i].m_energy;
				energy_tot[(int)interf.BEE_Energy_T.EnergyAmount_ET][i] = outputs[i].m_energyTotal;
				energy_cat[(int)interf.BEE_Energy_T.EnergyCost_ET][i] = outputs[i].m_energyCost;
				energy_tot[(int)interf.BEE_Energy_T.EnergyCost_ET][i] = outputs[i].m_energyTotalCost;
			}
		}

		/// <summary>
		/// overloading function, to output one stage total energy output data.
		/// </summary>
		/// <param name="output"></param>
		/// <returns></returns>
		private static double[] outputEnergy(Output output_in)
		{
			output_in.unitConversion();
			output_in.calcEnergyCost(m_energyRate);
			output_in.calcTotalEnergy();

			// construct output array
			double[] energy_tot = new double[(int)interf.BEE_Energy_T.NUM_ET];

			// transfer data to output array.
			energy_tot[(int)interf.BEE_Energy_T.EnergyAmount_ET] = output_in.m_energyTotal[(int)Keyword.energy_form_t.Electricity_EFT]
				+ output_in.m_energyTotal[(int)Keyword.energy_form_t.Gas_EFT] / 3.6;
			energy_tot[(int)interf.BEE_Energy_T.EnergyCost_ET] = output_in.m_energyTotalCost[(int)Keyword.energy_form_t.Electricity_EFT]
				+ output_in.m_energyTotalCost[(int)Keyword.energy_form_t.Gas_EFT];

			return energy_tot;
		}

		/// <summary>
		/// calculate original and retrofit energy difference
		/// </summary>
		/// <param name="ecm_cost">ECM cost array</param>
		/// <param name="orig_total">original total energy (electricity, natural gas)</param>
		/// <param name="retro_total">retrofit total energy (electricity, natural gas)</param>
		/// <returns>economic output (total energy saving, and total ECM cost)</returns>
		private static double[] diffOutEnergy(double[] economic_opt, double[] ecm_cost, double orig_total, double retro_total)
		{
			double[] results = new double[(int)Cost.BEE_Economic_T.NUM_ET];

			// ECM cost
//			for(int i = 0; i < ecm_cost.Length; ++i)
//			{
//				cost[(int)Cost.BEE_Economic_T.EcmCost_ET] += ecm_cost[i];
//			}

			results[(int)Cost.BEE_EconomyResult_T.TotalCost_ERT] = (ecm_cost[(int)Cost.BEE_EcmCostCat_T.Labor_ECCT] + ecm_cost[(int)Cost.BEE_EcmCostCat_T.Material_ECCT]) +
				+Algorithm.calculateDiscountSum(ecm_cost[(int)Cost.BEE_EcmCostCat_T.OM_ECCT], 
					economic_opt[(int)Building.BEE_BuildingEconomic_T.DiscountRate_BET], economic_opt[(int)Building.BEE_BuildingEconomic_T.EvaluationPeriod_BET]);

			// energy saving
			results[(int)Cost.BEE_Economic_T.EnergySaving_ET] = Algorithm.calculateDiscountSum(orig_total - retro_total,
				economic_opt[(int)Building.BEE_BuildingEconomic_T.DiscountRate_BET], economic_opt[(int)Building.BEE_BuildingEconomic_T.EvaluationPeriod_BET]);

			return results;
		}

		/// <summary>
		/// copy error list elements to a string[] error message array
		/// </summary>
		/// <param name="err_list">error list input in</param>
		/// <return>string[] type error message for output</return>
		private static string[] copyErrMsg(List<string> err_list)
		{
			string[] err_msgs = new string[err_list.Count];
			for (int i = 0; i < err_list.Count; ++i)
			{
				err_msgs[i] = err_list[i];
			}
			return err_msgs;
		}

		/// <summary>
		/// append error list to error messages
		/// </summary>
		/// <param name="err_msgs">error message array reference</param>
		/// <param name="err_list">error list</param>
        private static void appendErrMsg(ref string[] err_msgs, List<string> err_list)
        {
            int err_len = err_msgs.Length;
            Array.Resize(ref err_msgs, (err_list.Count + err_len));

            for (int i = 0; i < err_list.Count; ++i)
            {
                err_msgs[err_len + i] = err_list[i];
            }
        }

        /// <summary>
        /// copy error list elements to a string[] error message array
        /// </summary>
        /// <param name="err_msg">error message string[] input in</param>
        /// <param name="err_list">string list variable to append</param>
        private static void appendErrMsgToList(string[] err_msg, List<string> err_list)
        {
            for (int i = 0; i < err_msg.Length; ++i)
            {
                err_list.Add(err_msg[i]);
            }
        }


		/// *********** version 0.2 functions below ************ ///

		/// <summary>
		/// create measure objects per building types
		/// </summary>
		/// <param name="system_str">system string to setup system information. see iterf.BEE_SystemInput_T</param>
		/// <param name="err_msg">error message string[] input in</param>
		/// <param name="building_type">building type, access via building</param>
		public static void initializeProgram(string[] system_str, string[] building_opt, double[] building_input, string[] opt_str, ref string[] err_msg)
		{
			List<string> err_list = new List<string>();

			// initialize the program index.
			m_options = indexingOptions(opt_str, err_list);

//			Console.WriteLine("Finish indexing input options");

			// initialize query of the program.
			m_query.setConnWords(system_str[(int)interf.BEE_SystemInput_T.ProgramPath_ST]);
			m_query.connect();

//			Console.WriteLine("Finish connecting database");

			// query measures
			m_measureList = new List<DmMeasure>();
			m_dictionaryList = new Dictionary<string, int>();
			m_query.queryMeasures((interf.BEE_Building_T)m_options[(int)interf.BEE_AnalysisOption_T.BuildingType_AOT], m_measureList, m_dictionaryList, err_list);
			m_groupList = new List<DmMeasureGroup>();
			m_groupDictionaryList = new Dictionary<string, int>();
			m_query.queryMeasureGroups((interf.BEE_Building_T)m_options[(int)interf.BEE_AnalysisOption_T.BuildingType_AOT], m_groupList, m_groupDictionaryList, err_list);

//			Console.WriteLine("Finish querying measures");

			// query energy rate
			queryEnergyRate(err_list);

//			Console.WriteLine("Finish querying energy rate");

			prepareBuildingArea(building_input);

			if (err_list.Count > 0 )
				err_msg = copyErrMsg(err_list);
		}

		/// <summary>
		/// terminate program after calculation.
		/// call this function when we go back to the first page and start another new building analysis.
		/// </summary>
		public static void terminateProgram()
		{
			m_query.disconnect();

			m_options = null;
			m_factor = null;
			m_areas = null;
			m_ratios = null;
			m_energyRate = null;
			m_query = null;
			m_al = null;
			m_measureList = null;
			m_dictionaryList = null;
			m_groupList = null;
			m_groupDictionaryList = null;

			GC.Collect();
			GC.WaitForPendingFinalizers();

			m_factor = new double[(int)Keyword.energy_cat_t.NUM_ECT];
			m_areas = new double[(int)Building.BEE_BuildingFloorArea_T.NUM_BFAT];
			m_ratios = new double[(int)Building.BEE_BuildingFloorArea_T.NUM_BFAT];
			m_energyRate = new double[(int)Keyword.energy_form_t.NUM_EFT];
			m_query = new Query();
			m_al = new Algorithm(ref m_query);
			m_measureList = new List<DmMeasure>();
			m_dictionaryList = new Dictionary<string, int>();
			m_groupList = new List<DmMeasureGroup>();
			m_groupDictionaryList = new Dictionary<string, int>();
		}

		/// <summary>
		/// get one measure structure based on measure code
		/// </summary>
		/// <param name="code">measure code</param>
		/// <param name="err_msg">error message to return</param>
		/// <returns>the measure structure</returns>
		public static measure getOneMeasure(string code, ref string[] err_msg)
		{
			measure m =  new measure();

			// Check if the code is contained in the dictionary list
			if (m_dictionaryList.ContainsKey(code))
			{
				int index = m_dictionaryList[code];			// get measure's index
				DmMeasure measure = m_measureList[index];

				m.code = measure.dm_code;
				m.description = new string[(int)interf.BEE_Language_T.NUM_LT];
				m.description[(int)interf.BEE_Language_T.English_LT] = measure.dm_description;
				m.description[(int)interf.BEE_Language_T.Chinese_LT] = measure.dm_descriptionCN;
				m.text = new string[(int)interf.BEE_Language_T.NUM_LT];
				m.text[(int)interf.BEE_Language_T.English_LT] = measure.dm_text;
				m.text[(int)interf.BEE_Language_T.Chinese_LT] = measure.dm_textCN;
				m.options = new string[(int)interf.BEE_Language_T.NUM_LT][];
				if (measure.dm_options != null)
				{
					m.options[(int)interf.BEE_Language_T.English_LT] = measure.dm_options.ToArray();
					m.options[(int)interf.BEE_Language_T.Chinese_LT] = measure.dm_optionsCN.ToArray();
					m.numOptions = measure.dm_options.Count;
					m.defaultValue = "0";	// the first option for each ECM is always the default value
				}
				else 
				{ 
					if (measure.dm_code == "050101")		// boilor
						m.defaultValue = "0.7";
					else if (measure.dm_code == "050201")	// chiller
						m.defaultValue = "4.6";
				}
				m.control = measure.dm_control;
				m.lowcost = measure.dm_lowCost;
				// debug, to verify cost data only
//				m.cost = new double[3];
//				m.cost[0] = Convert.ToDouble(measure.dm_materialCost[1]);
//				m.cost[1] = Convert.ToDouble(measure.dm_laborHour);
//				m.cost[2] = Convert.ToDouble(measure.dm_omCost);
			}
			else 
			{
				List<string> err_list = new List<string>();
				err_list.Add("Can't not find related measure based on measure code input. Please check your measure code.");
				appendErrMsgToList(err_msg, err_list);
			}

			return m;
		}

		/// <summary>
		/// get one measure group structure based on measure group code
		/// </summary>
		/// <param name="code"></param>
		/// <param name="err_msg"></param>
		/// <returns></returns>
		public static group getOneGroup(string code, ref string[] err_msg)
		{
			group g = new group();

			// Check if the code is contained in the dictionary list
			if (m_groupDictionaryList.ContainsKey(code))
			{
				int index = m_groupDictionaryList[code];			// get measure's index
				DmMeasureGroup group = m_groupList[index];

				g.code = group.dm_code;
				g.text = new string[(int)interf.BEE_Language_T.NUM_LT];
				g.text[(int)interf.BEE_Language_T.English_LT] = group.dm_groupName;
				g.text[(int)interf.BEE_Language_T.Chinese_LT] = group.dm_groupNameCN;
			}
			else
			{
				List<string> err_list = new List<string>();
				err_list.Add("Can't not find related measure based on measure group code input. Please check your measure group code.");
				appendErrMsgToList(err_msg, err_list);
			}

			return g;
		}


		/// <summary>
		/// parse original and retrofit inputs list
		/// </summary>
		/// <param name="codes"></param>
		/// <param name="inputs"></param>
		/// <param name="continuous"></param>
		/// <param name="empirical"></param>
		/// <param name="simulated"></param>
		/// <param name="err_list"></param>
		public static void parseInputs(string[][] inputs, double[] continuous, ref int[][] empirical, ref string[][] simulated, List<string> err_list)
		{
			for (int stage = 0; stage < (int)Keyword.stage_t.NUM_ST; ++stage)
			{
				parseAnInput(stage, inputs, continuous, ref empirical, ref simulated, err_list);
			}
		}

		/// <summary>
		/// parse an input
		/// </summary>
		/// <param name="stage"></param>
		/// <param name="inputs"></param>
		/// <param name="continuous"></param>
		/// <param name="empirical"></param>
		/// <param name="simulated"></param>
		/// <param name="err_list"></param>
		private static void parseAnInput(int stage, string[][] inputs, double[] continuous, ref int[][] empirical, ref string[][] simulated, List<string> err_list)
		{
			int building_type = m_options[(int)interf.BEE_AnalysisOption_T.BuildingType_AOT];

			double infiltration_factor = 0.0;

			bool infil_flag = false;

			// loop through each input measure code
			for (int i = 0; i < Keyword.m_input_code.Length; ++i)
			{
				if (m_dictionaryList.ContainsKey(Keyword.m_input_code[i]))
				{
					int index = m_dictionaryList[Keyword.m_input_code[i]];			// get measure's index
					// handle simulated measures
					if (m_measureList[index].dm_measureType == DmMeasure.MeasureType[(int)DmMeasure.measure_t.Simulate_MOT])
					{
						int sim_idx = Keyword.m_keyword.IndexOf(Keyword.m_input_code[i]);
						empirical[stage][index] = -1;	// we mark empirical arry -1 for all simulated variables.

						// special handling for wall U value
						if (Convert.ToInt32(Keyword.m_input_code[i]) == 30102)
						{
							if (stage == (int)Keyword.stage_t.ORIGINAL_ST)
								simulated[stage][sim_idx] = Convert.ToString(continuous[(int)interf.BEE_BuildingInput_T.WallU_BIT]);
							else
							{
								int dif_choice = Convert.ToInt32(inputs[(int)Keyword.stage_t.RETROFIT_ST][i]) - Convert.ToInt32(inputs[(int)Keyword.stage_t.ORIGINAL_ST][i]);
								if (dif_choice < 0)
									dif_choice = 0;
								double new_u = Algorithm.calculateNewUValue(continuous[(int)interf.BEE_BuildingInput_T.WallU_BIT], Algorithm.UValue_T.WALL_UVT, dif_choice * 0.04);
								if (new_u < 0.27)	// the minimum boundary for wall u value
									simulated[stage][sim_idx] = Convert.ToString(0.27);
								else
									simulated[stage][sim_idx] = Convert.ToString(new_u);
							}
							continue;
						}

						// special handling for roof U value
						else if (Convert.ToInt32(Keyword.m_input_code[i]) == 30302)
						{
							if (stage == (int)Keyword.stage_t.ORIGINAL_ST)
								simulated[stage][sim_idx] = Convert.ToString(continuous[(int)interf.BEE_BuildingInput_T.RoofU_BIT]);
							else
							{
								int dif_choice = Convert.ToInt32(inputs[(int)Keyword.stage_t.RETROFIT_ST][i]) - Convert.ToInt32(inputs[(int)Keyword.stage_t.ORIGINAL_ST][i]);
								if (dif_choice < 0)
									dif_choice = 0;
								double new_u = Algorithm.calculateNewUValue(continuous[(int)interf.BEE_BuildingInput_T.RoofU_BIT], Algorithm.UValue_T.ROOF_UVT, dif_choice * 0.04);
								if (new_u < 0.25)	// the minimum boundary for roof u value
									simulated[stage][sim_idx] = Convert.ToString(0.25);
								else
									simulated[stage][sim_idx] = Convert.ToString(new_u);
							}
							continue;
						}

						// special handling for infiltration
						else if (Convert.ToInt32(Keyword.m_input_code[i]) >= 30201 && Convert.ToInt32(Keyword.m_input_code[i]) <= 030210)
						{
							if (stage == (int)Keyword.stage_t.ORIGINAL_ST)
							{
								if (Convert.ToInt32(Keyword.m_input_code[i]) == 30201)
									simulated[stage][sim_idx] = Convert.ToString(continuous[(int)interf.BEE_BuildingInput_T.Infiltration_BIT]);
							}
							else
							{
								int dif_choice = Convert.ToInt32(inputs[(int)Keyword.stage_t.RETROFIT_ST][i]) - Convert.ToInt32(inputs[(int)Keyword.stage_t.ORIGINAL_ST][i]);
								if (dif_choice < 0)
									dif_choice = 0;
								infiltration_factor += Convert.ToDouble(m_measureList[index].dm_effects[dif_choice]);
							}
							continue;
						}

						// error handling
						else if (sim_idx < 0)
						{
							err_list.Add("Error happens when indexing input measure code: " + Keyword.m_input_code[i] + " to acqure simulated results.");
							return;
						}

						else
						{
							simulated[stage][sim_idx] = inputs[stage][i];

							// special handling for infiltration
							if (Convert.ToInt32(Keyword.m_input_code[i]) > 030210 && infil_flag == false)
							{
								simulated[stage][m_dictionaryList["030201"]] = Convert.ToString(Convert.ToDouble(continuous[(int)interf.BEE_BuildingInput_T.Infiltration_BIT]) * (1 - infiltration_factor));
								infil_flag = true;
							}
						}
					}

					// handle empirical measures
					else
					{
						int choice = Convert.ToInt32(inputs[stage][i]);

						empirical[stage][index] = choice;
					}

				}
				else
				{
					err_list.Add("Error happens when parsing input measure code: " + Keyword.m_input_code[i]);
					return;
				}
			}
		}

		/// <summary>
		/// calculate empirical energy calculation factor
		/// </summary>
		/// <param name="empirical"></param>
		/// <param name="factor"></param>
		/// <param name="err_list"></param>
		public static double[] calculateEnergyFactor(int[] empirical, List<string> err_list)
		{
			double[] factor = new double[(int)Keyword.energy_cat_t.NUM_ECT];

			for (int i = 0; i < m_measureList.Count; ++i)
			{
				int choice = empirical[i];

				if (choice < 0)
					continue;

				// influence factor
				double f = Convert.ToDouble(m_measureList[i].dm_effects[choice]);

				// influence energy factor modification for energy only take a portion of that category
				int index = (int)Building.BEE_strBuildingFloorArea.IndexOf(m_measureList[i].dm_appliedArea);
				if ( index >= (int)Building.BEE_BuildingFloorArea_T.LightingControl_BFAT)
				{
					f *= m_ratios[index];	// To apply the measure to specific area, revise the influence based on its area factor.
				}

				// we skip all simulated variables which has choice number = -1

				if (m_measureList[i].dm_influEnergy == null)
				{
					for (int j = 0; j < Keyword.m_energy_cat.Count; ++j)
					{
						factor[j] += f;
					}
				}
				else
				{
					// contribute the selected influence factor to each influence energy 
					// TODO: creat another table to use difference influence factor per building type, climate zone and energy category
					for (int j = 0; j < m_measureList[i].dm_influEnergy.Count; ++j)
					{
						int energy_cat_idx = Keyword.m_energy_cat.IndexOf(m_measureList[i].dm_influEnergy[j]);
						if (energy_cat_idx < 0)
						{
							err_list.Add("Error happens when indexing input measure code: " + m_measureList[i].dm_code + " to acqure energy category " + m_measureList[i].dm_influEnergy[j] + ".");
							return factor;
						}
						factor[energy_cat_idx] += f;
					}
				}
			}

			return factor;
		}

		/// <summary>
		/// Validate energy factor to make sure the retrofit factor is no lower than original factor.
		/// In other words, retrofit should always produce positive energy saving.
		/// </summary>
		/// <param name="factor"></param>
		public static void validateEnergyFactor(ref double[][] factor)
		{
			for (int i = 0; i < factor.Length; ++i)
			{
				if (factor[(int)Keyword.stage_t.ORIGINAL_ST][i] > factor[(int)Keyword.stage_t.RETROFIT_ST][i])
					factor[(int)Keyword.stage_t.RETROFIT_ST][i] = factor[(int)Keyword.stage_t.ORIGINAL_ST][i];
			}
		}

		/// <summary>
		/// this function create a string arry for a continuous variable by taking its lower, upper bound and interval.
		/// </summary>
		/// <param name="lower"></param>
		/// <param name="upper"></param>
		/// <param name="interval"></param>
		/// <returns></returns>
		public static string[] prepareContinuousStrings(double lower, double upper, double interval)
		{ 
			int len = Convert.ToInt32((upper - lower) / interval);
			string[] strs = new string[len + 1];

			for(int i = 0; i <= len; ++i)
			{
				strs[i] = Convert.ToString(lower + i * interval);
			}

			return strs;
		}

		/// <summary>
		/// prepare discrete string by know the string length
		/// </summary>
		/// <param name="len"></param>
		/// <returns></returns>
		public static string[] prepareDiscreteStrings(int len)
		{
			string[] strs = new string[len];
			for (int i = 0; i < len; ++i)
			{ 
				strs[i] = Convert.ToString(i);
			}

			return strs;
		}


		// ************* this is the test function below ******************** // 

		/// <summary>
		/// print array function simply combining all the outputs in one line.
		/// <param name="arr">the input array</param>
		/// </summary>
		private static void printArray(double[] arr)
		{
			string line = "Total: " + arr.Length + " -- ";

			for (int j = 0; j < (int)arr.Length; ++j)
			{
				line = line + arr[j] + "; ";
			}
			Console.WriteLine(line);
		}

		/// <summary>
		/// overloading, print array function simply combining all the outputs in one line.
		/// <param name="arr">the input array</param>
		/// </summary>
		private static void printArray(int[] arr)
		{
			string line = "Total: " + arr.Length + " -- ";

			for (int j = 0; j < (int)arr.Length; ++j)
			{
				line = line + arr[j] + "; ";
			}
			Console.WriteLine(line);
		}

		/// <summary>
		/// overloading, print array function simply combining all the outputs in one line.
		/// <param name="arr">the input array</param>
		/// </summary>
		private static void printArray(string[] arr)
		{
			string line = "Total: " + arr.Length + " -- ";

			for (int j = 0; j < (int)arr.Length; ++j)
			{
				line = line + arr[j] + "; ";
			}
			Console.WriteLine(line);
		}

	}
}
